package ch.zhaw.cctd.logic.util;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Verantwortlich für die Konfiguration des Spiels. Parst Commandline Argumente,
 * lädt das Config File falls vorhanden und Managed die Settings falls die
 * selben Settings aus unterschiedlichen Quellen existieren
 * 
 * @author Rolf
 * 
 */
public class ConfigHandler {

	// Default Config File
	private static final String CFGFILE = "cctd.properties";

	// ---------------- CCTD PARAMETER NAMES ------------
	/**
	 * Properties Parameter: XML Path for Output
	 */
	public static final String CFG_PAR_XML = "XML";
	public static final String CFG_PAR_XML_DESC = "Path to a custom cctd xml file";
	private static final String CFG_PAR_XML_DEF = "../xml/cctd.xml";
	private static final String CFG_PAR_XML_MATCH = "^(.*?/|.*?\\\\)?([^\\./|^\\.\\\\]+)(?:\\.([^\\\\]*)|)+(\\.[^\\/\\\\:*?\"<>|]+)$";

	public static final String CFG_PAR_RES = "RESOLUTION";
	public static final String CFG_PAR_RES_DESC = "Resolution of the Game Windows (Format: 1024x768)";
	private static final String CFG_PAR_RES_DEF = "1024x768";
	private static final String CFG_PAR_RES_MATCH = "\\d*x\\d*";

	// ----------------------------------------------------
	public static final String[][] OPTIONSET = { { CFG_PAR_XML, CFG_PAR_XML_DESC, CFG_PAR_XML_DEF, CFG_PAR_XML_MATCH },
			{ CFG_PAR_RES, CFG_PAR_RES_DESC, CFG_PAR_RES_DEF, CFG_PAR_RES_MATCH } };
	// ----------------------------------------------------

	// LOCAL STATIC VARS
	private static Logger logger = LoggerFactory.getLogger(ConfigHandler.class);

	/**
	 * Current Instance from Singleton
	 */
	private static ConfigHandler instance;

	// INSTANCE VARIABLES
	private Properties properties;

	/**
	 * Singleton: Create Properties with Default Settings
	 */
	protected ConfigHandler() {
		properties = new Properties();
	}

	/**
	 * Singleton Get Instance
	 * 
	 * @return Instance of the configurator
	 */
	public static ConfigHandler getInstance() {
		if (instance == null) {
			instance = new ConfigHandler();
		}
		return instance;
	}

	public static void reset() {
		instance = null;
	}

	/**
	 * Get a specified parameter value
	 * 
	 * @param param
	 *            Parameter Name as String (Use the Static Variables)
	 * @return Parameter Value
	 */
	public String getConfigValue(String param) {
		String prop = properties.getProperty(param);
		logger.trace("Returning Arg: {} Value: {}", param, prop);
		return prop;
	}

	/**
	 * Get all Properties stored in the Configurator
	 * 
	 * @return Properties Object
	 */
	public Properties getProperties() {
		return properties;
	}

	@SuppressWarnings("static-access")
	public void loadArgs(String[] args) {

		// first Load Config File (Priority 2 Settings)
		loadCfgFile();

		// Set Options for DataTransporter
		Options options = new Options();

		options.addOption("?", "help", false, "Show available parameters.");
		for (String[] ss : OPTIONSET) {
			options.addOption(OptionBuilder.withArgName(ss[0].toLowerCase()).hasArg().withDescription(ss[1]).withLongOpt(ss[0].toLowerCase())
					.create(ss[0].toLowerCase().substring(0, 1)));
		}
		// Parse Args
		parseArgs(options, args);

		// Write CFg File
		writeCfgFile();
	}

	private void parseArgs(Options options, String[] args) {
		CommandLineParser parser = new PosixParser();
		CommandLine cmd;
		logger.trace("Parsing: {}", Arrays.toString(args));
		try {
			cmd = parser.parse(options, args);

			// Put all Arguments in the Properties Objects
			for (String[] ss : OPTIONSET) {
				// If Param does not exist, use default Value in ss[2]
				String entry = cmd.getOptionValue(ss[0].toLowerCase(), ss[2]);
				if (entry.matches(ss[3])) {
					logger.trace("Argument: {} Value: {}", ss[0], entry);
					properties.put(ss[0], entry);
				} else {
					logger.error("Wrong Argument Value: {}", ss[0]);
					throw new IllegalArgumentException("Parameter " + ss[0] + " has an invalid value");
				}
			}
			if (cmd.hasOption("?")) {
				HelpFormatter formatter = new HelpFormatter();
				formatter.printHelp("DataTransporter.class ", options);
			}
		} catch (ParseException e) {
			logger.error("Error parsing arguments: {}", e.getMessage());
			HelpFormatter formatter = new HelpFormatter();
			formatter.printHelp("DataTransporter.class ", options);
		}

	}

	private void loadCfgFile() {
		// first Load Config File (Priority 2 Settings)
		try {
			this.properties.load(new FileInputStream(ConfigHandler.CFGFILE));
		} catch (FileNotFoundException e1) {
			// No error since this file may not exist
			logger.trace("Uncritical FileNotFoundException: {}", e1.getMessage());
		} catch (IOException e1) {
			// No Error since an IOException may happen if the application is
			// used in a readonly environment
			logger.trace("Uncritical IOException: {}", e1.getMessage());
		}
	}

	private void writeCfgFile() {
		// Store the File if Trace is enabled
		if (logger.isTraceEnabled()) {
			// FIXME: Was machen wir damit?
			/*
			 * try { logger.trace(properties.toString());
			 * 
			 * properties.store(new FileWriter(new File(Configurator.CFGFILE)),
			 * "Wiki Doclet"); } catch (IOException e) {
			 * logger.trace("Error Writing the Config File", e); }
			 */
		}
	}
}
